#! /usr/bin/python

# Client
import sys
from socket import *
import httplib, urllib

# Socket Parameter festlegen
host = '239.255.255.250'
port = 1900
buf = 1024
addr = (host,port)
timeout = 50

# Erstellen des Sockets
s = socket(AF_INET,SOCK_DGRAM)

def httpget(addr, URL):
	"""URL must be provided as reltive path to addr, e.g. /index.htm in  http://192.168.178.21:0000/index.htm"""
	# form contents
	conn = httplib.HTTPConnection(addr)
	conn.request("GET", URL)
	r = conn.getresponse()
	#print r.status, r.reason # "200 OK"
	data = None
	if r.status == 200:	data = r.read()
	conn.close()
	return data

def httppost(addr,SCPDURL, data, headers):
	# form contents
	"""form data must be prepared using :  urllib.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0}) """
	#print headers
	#print data
	conn = httplib.HTTPConnection(addr)
	conn.request("POST", SCPDURL, data, headers)
	response = conn.getresponse()
	#print response.status, response.reason
	data = response.read()
	conn.close()
	return data

def XmlValue(tag, data):
	start = data.find("<"+tag+">")
	if start > -1:
		return data[start+len(tag)+2:data.find("</"+tag+">", start)]

	return None


class Service:
	def __init__(self, data):
		self.serviceType = XmlValue("serviceType", data)
		self.serviceId	 = XmlValue("serviceId"  , data)
		self.SCPDURL	 = XmlValue("SCPDURL"    , data)
		self.controlURL	 = XmlValue("controlURL" , data)
		self.eventSubURL = XmlValue("eventSubURL", data)

	def info(self):
		print "\tservicType :", self.serviceType
		print "\tserviceId  :", self.serviceId
		print "\tSCPDURL    :", self.SCPDURL
		print "\tcontrolURL :", self.controlURL
		print "\teventSubURL:", self.eventSubURL

def getServiceList(data):
		services = []
		#s = XmlValue("service", data);
		#print s
		start = data.find("<service>", 0)
		while start > 0:
			end = data.find("</service>", start+1)
			services.append(Service(data[start+9:end]))
			start = data.find("<service>", end)
		return services


class MediaRenderer:
	def __init__(self, discovery_header):
		self.location = None
		self.xmlpath  = None
		self.usn	  = None
		self.services = []
		lines = discovery_header.split("\n");
		for l in lines:

			if l.lower().startswith("location: "):
				self.location = l[10:l.rfind('/')].strip().strip("http://");
				self.xmlpath  = l[l.rfind('/'):].strip();
				continue;
			if l.lower().startswith("usn: "):
				self.usn = l[5:].strip();
				continue;

		data = httpget(self.location, self.xmlpath);
		self.name = XmlValue("friendlyName", data)
		self.services = getServiceList(data)

	def GetVolume(self):
		for i in self.services:
			if i.serviceId == "urn:upnp-org:serviceId:RenderingControl":
				data = """
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    <s:Header>
    </s:Header>
    <s:Body>
    <u:GetVolume xmlns:u="urn:schemas-upnp-org:service:RenderingControl:1">
    <InstanceID>0</InstanceID>
    <Channel>Master</Channel>
    </u:GetVolume>
    </s:Body>
</s:Envelope> """
				headers = {"Content-type": "text/xml", "charset": "utf-8",'SOAPAction':'urn:schemas-upnp-org:service:RenderingControl:1#GetVolume'}
				data = httppost(self.location, i.controlURL, data, headers);
				return float(XmlValue("CurrentVolume", data));

	def SetVolume(self, value):
		for i in self.services:
			if i.serviceId == "urn:upnp-org:serviceId:RenderingControl":
				data = """
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    <s:Header>
    </s:Header>
    <s:Body>
    <u:SetVolume xmlns:u="urn:schemas-upnp-org:service:RenderingControl:1">
    <InstanceID>0</InstanceID>
    <Channel>Master</Channel>
    <DesiredVolume>%d</DesiredVolume>
    </u:SetVolume>
    </s:Body>
</s:Envelope> """ % (int(value))
				headers = {"Content-type": "text/xml", "charset": "utf-8",'SOAPAction':'urn:schemas-upnp-org:service:RenderingControl:1#SetVolume'}
				httppost(self.location, i.controlURL, data, headers);

	def info(self):
		print 28*'#',"Media Renderer", 28 * '#'
		print "friendlyName:",self.name
		print "Location    :",self.location
		print "xmlpath     :",self.xmlpath
		print "USN         :",self.usn
		print "Services    :",
		for i in range(len(self.services)):
			print #i
			self.services[i].info()

class MediaServer:
	def __init__(self, discovery_header):
		self.location = None
		self.xmlpath  = None
		self.usn	  = None
		self.services = []
		lines = discovery_header.split("\n");

		for l in lines:
			if l.lower().startswith("location: "):
				self.location = l[10:l.rfind('/')].strip().strip("http://");
				self.xmlpath  = l[l.rfind('/'):].strip();
				continue;
			if l.startswith("USN: "):
				self.usn = l[5:].strip();
				continue;

		data = httpget(self.location, self.xmlpath);
		self.name = XmlValue("friendlyName", data)
		self.services = getServiceList(data)

	def info(self):
		print 28*'#',"Media Server", 28 * '#'
		print "friendlyName:",self.name
		print "Location    :",self.location
		print "xmlpath     :",self.xmlpath
		print "USN         :",self.usn
		print "Services    :",
		for i in range(len(self.services)):
			print #i
			self.services[i].info()

def init_MediaRenderer():
	print "Searching Media Renderer"
	timeout = 50
	#SSDP Discovery request
	# all devices
	#data = "M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: \"ssdp:discover\"\r\nMX: 3\r\nST: upnp:rootdevice\r\n\r\n"
	# only media renderer
	data = "M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: \"ssdp:discover\"\r\nMX: 3\r\nST: urn:schemas-upnp-org:device:MediaRenderer:1\r\n\r\n"
	#Ausgabe des zu sendenden Requests
	#print data

	try: 	s.sendto (data, ("239.255.255.250", 1900))
	except:	print "UPnP gateways unreachable"

	Renderer = []
	while 1:
		s.settimeout (timeout)
		try:
			data, addr = s.recvfrom (4096)
			Renderer.append(MediaRenderer(data))
		except:
			break
		timeout = max (timeout * 0.5, 0.01)
	for i in Renderer: i.info();
	return Renderer

def init_MediaServer():
	print "Searching Media Server"
	timeout = 50
	#SSDP Discovery request
	# all devices
	#data = "M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: \"ssdp:discover\"\r\nMX: 3\r\nST: upnp:rootdevice\r\n\r\n"
	# only media Server
	data = "M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: \"ssdp:discover\"\r\nMX: 3\r\nST: urn:schemas-upnp-org:device:MediaServer:1\r\n\r\n"

	try: 	s.sendto (data, ("239.255.255.250", 1900))
	except:	print "UPnP gateways unreachable"

	Server = []
	while 1:
		s.settimeout (timeout)
		try:
			data, addr = s.recvfrom (4096)
			Server.append(MediaServer(data))
		except:
			break
		timeout = max (timeout * 0.5, 0.01)
	for i in Server: i.info();
	return Server

if __name__ == "__main__":
	for i in Renderer:
		if i.name == "Connector Wohnzimmer":
			for j in i.services:
				if j.serviceId == "urn:upnp-org:serviceId:RenderingControl":
					data = """
	<?xml version="1.0"?>
	<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
	    <s:Header>
	    </s:Header>
	    <s:Body>
	    <u:GetVolume xmlns:u="urn:schemas-upnp-org:service:RenderingControl:1">
	    <InstanceID>0</InstanceID>
	    <Channel>Master</Channel>
	    </u:GetVolume>
	    </s:Body>
	</s:Envelope> """
					headers = {"Content-type": "text/xml", "charset": "utf-8",'SOAPAction':'urn:schemas-upnp-org:service:RenderingControl:1#GetVolume'}
					print httppost(i.location, j.controlURL, data, headers);

					data = """
	<?xml version="1.0"?>
	<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
	    <s:Header>
	    </s:Header>
	    <s:Body>
	    <u:SetVolume xmlns:u="urn:schemas-upnp-org:service:RenderingControl:1">
	    <InstanceID>0</InstanceID>
	    <Channel>Master</Channel>
	    <DesiredVolume>45</DesiredVolume>
	    </u:SetVolume>
	    </s:Body>
	</s:Envelope> """
					headers = {"Content-type": "text/xml", "charset": "utf-8",'SOAPAction':'urn:schemas-upnp-org:service:RenderingControl:1#SetVolume'}
					print httppost(i.location, j.controlURL, data, headers);

	sys.exit();
	####################### search media server ####################################
	#SSDP Discovery request
	data = "M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: \"ssdp:discover\"\r\nMX: 3\r\nST: urn:schemas-upnp-org:device:MediaServer:1\r\n\r\n"
	#Ausgabe des zu sendenden Requests
	print data


	try:
		s.sendto (data, ("239.255.255.250", 1900))
	except:
		print "UPnP gateways unreachable"

	timeout = 50
	while 1:
		s.settimeout (timeout)
		try:
			data, addr = s.recvfrom (4096)
			print data
		except:
			break
		timeout = max (timeout * 0.5, 0.01)


	#http://192.168.178.21:52629/cd/Control


	# Close socket
	s.close()


